package com.redxun.auth;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.redxun.auth.service.IUserDetailsService;
import com.redxun.core.api.org.ICoreUserService;
import com.redxun.core.common.constant.MBoolean;
import com.redxun.core.common.constant.SecurityConstants;
import com.redxun.core.common.entity.JsonResult;
import com.redxun.core.common.model.DefaultUser;
import com.redxun.core.common.utils.ContextUtil;
import com.redxun.core.common.utils.I18nUtil;
import com.redxun.core.db.util.CommonDao;
import com.redxun.core.dto.user.OsPasswordPolicyDto;
import com.redxun.core.dto.user.OsUserDto;
import com.redxun.core.tool.BeanUtil;
import com.redxun.core.tool.StringUtils;
import com.redxun.core.tool.constant.CommonConstant;
import com.redxun.service.api.user.IUserService;
import com.redxun.share.dto.FormUserConstant;
import com.redxun.share.dto.FormUserMappingDto;
import com.redxun.share.service.form.FormUserRedisServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.social.security.SocialUserDetails;
import org.springframework.social.security.SocialUserDetailsService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 通过用户名与密码提供用户身份认证服务类.
 *
 * @author yjy
 */
@Slf4j
@Service
public class UserDetailServiceImpl implements IUserDetailsService, SocialUserDetailsService {

    @Autowired
    private ICoreUserService userService;

    @Autowired
    private ICoreUserService osPasswordPolicyService;


    @Autowired
    ICoreUserService orgClient;

    @Resource
    FormUserRedisServiceImpl formUserRedisService;

    @Resource
    IUserService userServiceImpl;

    private static final String ACCOUNT_TYPE = SecurityConstants.DEF_ACCOUNT_TYPE;

    /**
     * 根据用户账号或其他登录信息获取用户对象.
     * @param username
     * @return
     */
    @Override
    public UserDetails loadUserByUsername(String username) {

        OsUserDto osUserDto=null;
        //处理外部用户.
        if (username.indexOf(FormUserConstant.USERNAME_TOKEN) != -1) {
            osUserDto = getByExtUser(username);
        } else {
            osUserDto = getByUserName(username);
        }

        if (osUserDto == null || StringUtils.isEmpty(osUserDto.getUserId())) {
            throw new InternalAuthenticationServiceException(OsUserDto.ERROR_TYPE_ACCOUNT);
        }
        DefaultUser paasUser = new DefaultUser();
        BeanUtil.copyProperties(paasUser, osUserDto);
        //设置当前用户，用于记录登录人日志.
        ContextUtil.setCurrentUser(paasUser);
        ContextUtil.setExtUser(osUserDto.getExtUserMap());
        //处理密码策略
        handPasswordPolicy(paasUser);
        //设置外部用户.
        boolean isExtUser=BeanUtil.isNotEmpty(osUserDto.getExtUserMap());
        paasUser.setExtUser(isExtUser);

        return checkUser(paasUser);
    }

    /**
     * 根据外部用户获取对应的用户对象.
     * @param username  用户账号或其他的登录信息.
     * @return
     */
    private OsUserDto getByExtUser(String username){
        Integer idx = username.indexOf(FormUserConstant.USERNAME_TOKEN);
        //账号
        String alias= username.substring(0,idx);
        //应用
        String appAlias= username.substring(idx +4 );

        JsonResult<FormUserMappingDto> result = formUserRedisService.getByAppAlias(appAlias);

        if(!result.isSuccess()){
            throw new InternalAuthenticationServiceException("请输入正确的应用别名!");
        }

        FormUserMappingDto formUserMappingDto=result.getData();
        OsUserDto osUserDto = userServiceImpl.getUserByFormUserMapping(alias,formUserMappingDto);

        return osUserDto;
    }



    /**
     * 根据用户名获取用户对象.
     * @param username
     * @return
     */
    private OsUserDto getByUserName(String username) {
        // 备份原有完整的username
        String userNameCopy = new String(username);
        String tenantId = CommonConstant.PLATFORM_TENANT_ID_VAL;
        OsUserDto osUserDto = userService.findByUsernameAndTenantId(username, tenantId);
        if (BeanUtil.isNotEmpty(osUserDto)) {
            return osUserDto;
        }
        // admin@redxun.cn
        int index = username.lastIndexOf("@");
        //带有@，表示登录的用户名称是以 用户名@机构域名 组成的值.
        if (index > -1) {
            //机构域名
            String domain = username.substring(index + 1);
            username = username.substring(0, index);
            // 判断domain是否为空，为空不查
            if (StringUtils.isNotEmpty(domain)) {

                int tenantSymbol = userNameCopy.lastIndexOf(OsUserDto.TENANT_SYMBOL);
                // 此时tenantUserName代表的是 @ 之前的字符
                String tenantUserName = username;
                if (tenantSymbol > -1) {
                    // 取出@ 跟 ##^^^中间的 域名
                    domain = userNameCopy.substring(index + 1, tenantSymbol);
                    // 此时tenantUserName代表的是 ##^^^ 之前的字符
                    tenantUserName = userNameCopy.substring(0, tenantSymbol);
                }
                //机构ID
                JsonResult result = userService.findTenantIdByDomain(domain);
                try {
                    //处理租户.
                    handTenant(result);
                    tenantId = result.getMessage();
                } catch (InternalAuthenticationServiceException e) {
                    // 找不到域名，使用平台租户ID
                    tenantId = CommonConstant.PLATFORM_TENANT_ID_VAL;
                    username = tenantUserName;
                    log.error(e.getMessage());
                }
            } else {
                username = userNameCopy;
            }

        } else {
            index = username.indexOf(OsUserDto.TENANT_SYMBOL);
            if (index != -1) {
                tenantId = username.substring(index + OsUserDto.TENANT_SYMBOL.length());
                username = username.substring(0, index);
            }

        }
        //获取登录用户
        osUserDto = userService.findByUsernameAndTenantId(username, tenantId);

        return osUserDto;
    }

    /**
     * 处理租户出错的信息.
     *
     * @param result result
     */
    private void handTenant(JsonResult result) {
        if (result.isSuccess()) {
            return;
        }
        String message = result.getMessage();
        throw new InternalAuthenticationServiceException(message);
    }

    /**
     * 处理密码策略.
     * <pre>
     *     1.处理首次登录.
     *     2.处理密码过期.
     *     3.处理账号锁定.
     * </pre>
     *
     * @param paasUser paasUser
     */
    void  handPasswordPolicy(DefaultUser paasUser) {
        boolean isAdmin=paasUser.isTenantAdmin() || paasUser.isRootAdmin();
        if (isAdmin) {
            return;
        }

        //不是超管获或租户管理员
        String username = paasUser.getAccount();
        String tenantId = paasUser.getTenantId();
        //首次登录是否需要修改密码.
        OsPasswordPolicyDto policyDto = osPasswordPolicyService.getByTenantId(tenantId);
        if (StringUtils.isEmpty(policyDto.getId())) {
            return;
        }

        //密码过期导致账号被锁定
        if (userService.isLockedByPasswordExpire(username, tenantId)) {
            throw new InternalAuthenticationServiceException(OsUserDto.ERROR_TYPE_EXPIRE);
        }
        //密码多次错误输入导致账号被锁定
        if (userService.isLockedByPasswordInputError(username, tenantId)) {
            throw new InternalAuthenticationServiceException(OsUserDto.ERROR_TYPE_ERROR_INPUT);
        }
        // 是否首次登录
        if (BeanUtil.isEmpty(paasUser.getIsFirstLogin())) {
            // 读取站点配置关于是否首次登录.
            String needFirstLogin = getIsFirstLogin(policyDto);
            paasUser.setIsFirstLogin(needFirstLogin);
        }


    }

    /**
     * 获取配置是否需要首次登录.
     *
     * @param policy policy
     * @return
     */
    private String getIsFirstLogin(OsPasswordPolicyDto policy) {
        if (StringUtils.isEmpty(policy.getIsEnabled()) || MBoolean.NO.name().equals(policy.getIsEnabled())) {
            return MBoolean.NO.name();
        }
        return policy.getIsFirstLoginUpdate();
    }

    /**
     * 通过OpenID加载用户身份.
     *
     * @param openId openId
     * @return
     */
    @Override
    public SocialUserDetails loadUserByUserId(String openId) {
        OsUserDto osUserDto = userService.findByOpenId("", openId, 6);
        if (osUserDto == null) {
            throw new RuntimeException("can't find user from openId : " + openId);
        }
        DefaultUser defaultUser = new DefaultUser();
        BeanUtil.copyProperties(defaultUser, osUserDto);

        return defaultUser;
    }

    @Override
    public boolean supports(String accountType) {
        return ACCOUNT_TYPE.equals(accountType);
    }

    /**
     * 通过手机号加载用户身份.
     *
     * @param mobile mobile
     * @return
     */
    @Override
    public UserDetails loadUserByMobile(String mobile) {
        OsUserDto loginAppUser = userService.findByMobile(mobile);
        //设置上下文用户.
        ContextUtil.setCurrentUser(loginAppUser);
        if (loginAppUser == null) {
            throw new InternalAuthenticationServiceException(I18nUtil.i18n("mobileOrPwdError", "手机号或密码错误"));
        }
        DefaultUser defaultUser = new DefaultUser();
        BeanUtil.copyProperties(defaultUser, loginAppUser);

        return checkUser(defaultUser);
    }

    /**
     * 检查用户身份是否合法.
     *
     * @param defaultUser 用户实体
     * @return
     */
    private DefaultUser checkUser(DefaultUser defaultUser) {
        if (defaultUser  !=  null && !defaultUser.isEnabled()) {
            throw new DisabledException(I18nUtil.i18n("userHasInvalidated","用户已作废"));
        }

        defaultUser.setLockReason("");
        defaultUser.setPwdUpdateTime(null);

        return defaultUser;
    }
}
